#include "glwidget.h"

GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
    // Objetos
    Objects= new QVector <VObject *>;

    // Luces
    Lights= new QVector <VObject *>;

    // Sistemas de Particulas
    ParticleSystems= new QVector <ParticleSystem *>;

    // Vista
    AmbientColor.setX(0.4); AmbientColor.setY(0.4); AmbientColor.setZ(0.4);
    AmbientIntensity= 1.0;
    Crosshair= new QCursor (Qt::CrossCursor);
    setCursor(*Crosshair);
    GOANAGLYPH= false;
    RENDERWIREFRAME= false;
    OVERLAYWIREFRAME= false;

    // Para los assets!
    AssetsPath= "../assets/";

    // Controles!
    setFocusPolicy(Qt::StrongFocus);
    Controller= new Controls (QVector3D(1.2, 3.5, 7.2), 1.5707963267, 0.0, 45.0, 0.35, 5.0);
    Controller->setCDirection(QVector3D(-0.1, -0.3, -0.9));
    connect(this, SIGNAL(keypressed(int, QPoint)), Controller, SLOT(computeMatricesFromInputs(int, QPoint)));
    startTimer(10/60);
    setMouseTracking(false);
    TRACKMOUSE= false;

    //connect(this, SIGNAL(wheelmoved(int)), Controller, SLOT(computeMatricesFromWheel(int)));
    //connect(this, SIGNAL(rotateView(int,int)), controller, SLOT(computeMatricesFromRotation(int,int)));
}

void GLWidget::initializeGL()
{
    #define PROGRAM_VERTEX_ATTRIBUTE 0
    #define PROGRAM_NORMALS_ATTRIBUTE 1
    #define PROGRAM_TEXCOORD_ATTRIBUTE 2

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_POLYGON_OFFSET_FILL);

    glFrontFace(GL_CCW);

    // Una vez el contexto listo, vamos a crear la escena
    SetScene("../scenes/ParticleTestScene.bdv");
}

void GLWidget::paintGL()
{
    // Variables de control para determinados efectos
    int DualRender= 1;

    // Usaremos esta matriz auxiliar para no obtener errores en la estereovision por la doble llamada
    // a Controls::getProjectionMatrix()
    QMatrix4x4 PMatrix;
    QMatrix4x4 VMatrix;

    glClearColor(0.00, 0.00, 0.00, 1.0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

    if (RENDERWIREFRAME)
    {

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glPolygonOffset(1.0, 1.0);
    }
    else
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    if (GOANAGLYPH || OVERLAYWIREFRAME)
    {
        DualRender=0;
    }

    for (DualRender= DualRender; DualRender<2; DualRender++)
    {
        if (DualRender==0)
        {
            if (GOANAGLYPH)
            {
                glClear(GL_COLOR_BUFFER_BIT);

                // Renderizamos solo en Rojo
                glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
                glClear(GL_DEPTH_BUFFER_BIT);
            }
        }
        else
        {
            if (GOANAGLYPH)
            {
                // Renderizamos solo en Azul o en Cyan
                if (CYANANAGLYPH)
                    glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
                else
                    glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE);

                glClear(GL_DEPTH_BUFFER_BIT);
            }

            // Dibujamos los poligonos rellenos
            if (RENDERWIREFRAME && OVERLAYWIREFRAME)
            {
                glPolygonOffset(-10.0, -10.0);
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            }
        }

        // Una vez fijado el estado de OpenGL, procedemos a renderizar!
        PMatrix= Controller->getProjectionMatrix();
        VMatrix= Controller->getViewMatrix();

        for (int i=0; i<Objects->size(); i++)
        {
            if (Objects->at(i)->RENDERTHIS)
            {
                // Enviaremos la informacion de las lamparas
                for (int j=0; j<Lights->size(); j++)
                {
                    VObject* Current= Lights->at(j);
                    Objects->at(i)->SetLightInfo(AmbientColor, AmbientIntensity, Current->LightColor, Current->ObjPosition,
                                                 QVector3D(Current->constAtt, Current->linearAtt, Current->expAtt), Current->LightIntensity);
                }

                // Y finalmente renderizaremos los objetos
                if (RENDERWIREFRAME && OVERLAYWIREFRAME && DualRender==0) Objects->at(i)->OverlayWireframe();
                else Objects->at(i)->HideOverlayWireframe();

                Objects->at(i)->Render(PMatrix, VMatrix);
            }
        }

        for (int j=0; j<Lights->size(); j++)
        {
            // Se quiere dibujar la lampara?
            if (Lights->at(j)->RENDERTHIS)
            {
                Lights->at(j)->Render(PMatrix, VMatrix);
            }
        }

        // Ahora renderizamos los sistemas de particulas
        for (int k=0; k<ParticleSystems->size(); k++)
            ParticleSystems->at(k)->Render(PMatrix, VMatrix);
    }
}

void GLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);

    QPoint AuxRes= mapToGlobal(QPoint(w/2, h/2));
    Controller->parentResized(AuxRes.x(), AuxRes.y());

    updateGL();
}

// Esta funcion permite leer un vec3 con salto de linea de un archivo dado
float* GLWidget::ReadVec3(FILE *input)
{
    float *output= new float [3];

    fscanf(input, "%f/%f/%f\n", &output[0], &output[1], &output[2]);

    return output;
}

// Esta funcion permite leer un vec4 con salto de linea de un archivo dado
float* GLWidget::ReadVec4(FILE *input)
{
    float *output= new float [4];

    fscanf(input, "%f/%f/%f/%f\n", &output[0], &output[1], &output[2], &output[3]);

    return output;
}

// Esta funcion permite leer un float con salto de linea de un archivo dado
float GLWidget::ReadFloat(FILE *input)
{
    float output;

    fscanf(input, "%f\n", &output);

    return output;
}

// Esta funcion permite leer un int con salto de linea de un archivo dado
int GLWidget::ReadInt(FILE *input)
{
    int output;

    fscanf(input, "%d\n", &output);

    return output;
}


/** Escena -----------------------------------**/
void GLWidget::SetScene(QString url)
{
    // Primero vamos a limpiar la escena actual
    Objects->clear();
    Lights->clear();
    ParticleSystems->clear();
    AmbientColor.setX(1.0); AmbientColor.setY(1.0); AmbientColor.setZ(1.0);
    AmbientIntensity= 0.1;

    // Vamos a procesar el archivo de descripcion de escena
    FILE *input= fopen (url.toStdString().c_str(), "r");

    // Abrimos correctamente el archivo?
    if (input!= NULL)
    {
        // Vamos a procesar bloques hasta llegar al final de archivo
        while (1)
        {
            char line[128];

            int result= fscanf(input, "%s", line);

            if (result== EOF)
                break; // Final del archivo

            // Color ambiental
            if (strcmp(line, "#AC")== 0)
            {
                float* acolor= ReadVec3(input);
                AmbientColor.setX(acolor[0]);
                AmbientColor.setY(acolor[1]);
                AmbientColor.setZ(acolor[2]);
            }
            else if (strcmp(line, "#AI")== 0) // Intensidad de la luz ambiental
                AmbientIntensity= ReadFloat(input);
            else if (strcmp(line, "#LIGHT")== 0) // Una lampara, leamos su informacion
            {
                VObject* Light= new VObject();

                char lname [128];
                fscanf(input, "%s\n", lname);

                Light->setObjectName(QString(lname));
                Light->ThisIsLight();

                while (1)
                {
                    char lightline[128];
                    fscanf(input, "%s", lightline);

                    // Posicion de la Luz
                    if (strcmp(lightline, "#POSITION")== 0)
                    {
                        float* lpos= ReadVec3(input);
                        Light->SetPosition(lpos[0], lpos[1], lpos[2]);
                    }
                    else if (strcmp(lightline, "#COLOR")== 0) // Color de la Luz
                    {
                        float* lcol= ReadVec3(input);
                        Light->SetLightColor(lcol[0], lcol[1], lcol[2]);
                    }
                    else if (strcmp(lightline, "#INTENSITY")== 0) // Intensidad
                        Light->SetLightIntensity(ReadFloat(input));
                    else if (strcmp(lightline, "#LATT")== 0) // Atenuacion de la Luz
                    {
                        float* latt= ReadVec3(input);
                        Light->SetLightAtt(latt[0], latt[1], latt[2]);
                    }
                    else if (strcmp(lightline, "#SCALE")== 0) // Tamaño de la lampara
                        Light->ScaleObj(ReadFloat(input));
                    else if (strcmp(lightline, "#TRANSLATE")== 0) // Mover la lampara hacia...
                    {
                        float* ltra= ReadVec3(input);
                        Light->TranslateObj(ltra[0], ltra[1], ltra[2]);
                    }
                    else if (strcmp(lightline, "#ENDLIGHT")== 0)
                        break;
                }

                // Leida la informacion, toca cargar la lampara
                if (Light->LoadObjModel("../assets/models/abstractlight.obj"))
                {
                    if (Light->LoadShaders("../shaders/LightVShader.glsl", "../shaders/LightFShader.glsl"))
                    {
                        if (Light->PrepareShaderProgram())
                        {
                            Lights->append(Light);

                            // Informamos de la creacion de la luz
                            emit LightAdded(Light->objectName());

                            // Y conectamos nuestro widget al slot de movimiento de la luz
                            connect(this, SIGNAL(LightTweak(int)), Light, SLOT(LightTweak(int)));
                        }
                        else
                            printf("ERROR: No se pudieron compilar los shaders de %s\n", Light->objectName().toStdString().c_str());
                    }
                    else
                        printf("ERROR: No se pudieron cargar los shaders de %s\n", Light->objectName().toStdString().c_str());
                }
                else
                    printf("ERROR: No se pudo cargar el modelo de %s\n", Light->objectName().toStdString().c_str());
            }
            else if (strcmp(line, "/*")== 0) // Comentarios, ignorar
            {
                MARK:
                char ignore[256];
                fscanf(input, "%s\n", ignore);

                while (strcmp(ignore, "*/")!= 0)
                    goto MARK;
            }
            else if (strcmp(line, "#OBJ")== 0) // Viene un objeto!
            {
                VObject* Object= new VObject();

                char oname[128];
                fscanf(input, "%s\n", oname);
                short int ERRORS= 0;
                bool HASTEXTURE= false;

                Object->setObjectName(QString(oname));

                while (1)
                {
                    char objectline[128];
                    fscanf(input, "%s", objectline);

                    // Modelo del objeto
                    if (strcmp(objectline, "#MODEL")== 0)
                    {
                        char objmodel[128];

                        fscanf(input, "%s\n", objmodel);
                        if (!Object->LoadObjModel(AssetsPath + "models/" + QString(objmodel)))
                        {
                            ERRORS |= 1;
                            break;
                        }
                    }
                    else if (strcmp(objectline, "#TEXTURE")== 0) // Textura difusa del objeto
                    {
                        char objtexture[128];

                        fscanf(input, "%s\n", objtexture);
                        Object->SetDiffuseTexture(AssetsPath + "textures/" + QString(objtexture));
                        HASTEXTURE= true;
                    }
                    else if (strcmp(objectline, "#COLOR")== 0) // Color difuso del objeto
                    {
                        float* ocolor= ReadVec3(input);
                        Object->SetDiffuseColor(ocolor[0], ocolor[1], ocolor[2]);
                    }
                    else if (strcmp(objectline, "#TRANSLATE")== 0) // Mover el objeto hacia...
                    {
                        float* otra= ReadVec3(input);
                        Object->TranslateObj(otra[0], otra[1], otra[2]);
                    }
                    else if (strcmp(objectline, "#SCALE")== 0) // Tamaño del objeto...
                        Object->ScaleObj(ReadFloat(input));
                    else if (strcmp(objectline, "#SCALE3")== 0) // Tamaño preciso del objeto...
                    {
                        float* oscale= ReadVec3(input);
                        Object->ScaleObj(oscale[0], oscale[1], oscale[2]);
                    }
                    else if (strcmp(objectline, "#ENDOBJ")== 0)
                        break;
                }

                // Leida la informacion, toca cargar el objeto
                if (ERRORS & 1)
                {
                    printf("ERROR: No se pudo cargar el modelo de %s\n", Object->objectName().toStdString().c_str());
                }
                else
                {
                    if (Object->LoadShaders("../shaders/LameVShader.glsl", "../shaders/LameFShader.glsl"))
                    {
                        if (Object->PrepareShaderProgram())
                        {
                            if (HASTEXTURE)
                                Object->DiffuseTexture.THandler= bindTexture(QPixmap(Object->DiffuseTexture.TUrl));

                            Objects->append(Object);

                            // Informemos de la creacion del objeto
                            emit ObjectAdded(Object->objectName());
                        }
                        else
                            printf("ERROR: No se pudieron compilar los shaders de %s\n", Object->objectName().toStdString().c_str());
                    }
                    else
                        printf("ERROR: No se pudieron cargar los shaders de %s\n", Object->objectName().toStdString().c_str());
                }
            }
            else if (strcmp(line, "#PART")== 0) // Viene un sistema de particulas!
            {
                ParticleSystem* P= new ParticleSystem();

                char pname[128];
                fscanf(input, "%s\n", pname);
                short int Error= 0;
                bool HASTEXTURE= false;

                P->setName(QString(pname));

                while (1)
                {
                    char partline[128];
                    fscanf(input, "%s", partline);

                    if (strcmp(partline, "#STEPSIZE")== 0) // Velocidad de cambio
                        P->setStepSize(ReadFloat(input));
                    else if (strcmp(partline, "#STEPVAR")== 0) // Varianza de la velocidad
                        P->setStepVar(ReadFloat(input));
                    else if (strcmp(partline, "#SRC")== 0) // Origen del sistema
                    {
                        float* src= ReadVec3(input);
                        P->setSourcePosition(src[0], src[1], src[2]);
                    }
                    else if (strcmp(partline, "#SRCVAR")== 0) // Varianza del origen
                    {
                        float* srcv= ReadVec3(input);
                        P->setSourceVar(srcv[0], srcv[1], srcv[2]);
                    }
                    else if (strcmp(partline, "#SCOLOR")== 0) // Color de Origen del Objeto
                    {
                        float* scol= ReadVec4(input);
                        P->setStartColor(scol[0], scol[1], scol[2], scol[3]);
                    }
                    else if (strcmp(partline, "#SCOLORVAR")== 0) // Varianza del color de origen
                    {
                        float* scolv= ReadVec3(input);
                        P->setStartColorVar(scolv[0], scolv[1], scolv[2]);
                    }
                    else if (strcmp(partline, "#ECOLOR")== 0) // Color final del Objeto
                    {
                        float* ecol= ReadVec4(input);
                        P->setEndColor(ecol[0], ecol[1], ecol[2], ecol[3]);
                    }
                    else if (strcmp(partline, "#ECOLORVAR")== 0) // Varianza del color final
                    {
                        float* ecolv= ReadVec3(input);
                        P->setEndColorVar(ecolv[0], ecolv[1], ecolv[2]);
                    }
                    else if (strcmp(partline, "#DIR")== 0) // Direccion
                    {
                        float* dir= ReadVec3(input);
                        P->setDir(dir[0], dir[1], dir[2]);
                    }
                    else if (strcmp(partline, "#DIRVAR")== 0) // Varianza de la direccion
                    {
                        float* dirv= ReadVec3(input);
                        P->setDirVar(dirv[0], dirv[1], dirv[2]);
                    }
                    else if (strcmp(partline, "#SIZE")== 0) // Tamaño
                    {
                        float* size= ReadVec3(input);
                        P->setSize(size[0], size[1], size[2]);
                    }
                    else if (strcmp(partline, "#SIZEVAR")== 0) // Varianza del tamaño
                    {
                        float* sizev= ReadVec3(input);
                        P->setSizeVar(sizev[0], sizev[1], sizev[2]);
                    }
                    else if (strcmp(partline, "#TTL")== 0) // Tiempo de vida
                        P->setTimeToLive(ReadInt(input));
                    else if (strcmp(partline, "#TTLVAR")== 0) // Varianza del TTL
                        P->setTimeToLiveVar(ReadInt(input));
                    else if (strcmp(partline, "#AMOUNT")== 0) // Cantidad
                        P->setAmount(ReadInt(input));
                    else if (strcmp(partline, "#TEXTURE")== 0) // Textura asociada
                    {
                        char ptexture[128];

                        fscanf(input, "%s\n", ptexture);
                        P->setTextureURL(AssetsPath + "textures/" + QString(ptexture));
                        HASTEXTURE= true;
                    }
                    else if (strcmp(partline, "#BLENDING")== 0) // Blending
                    {
                        int src, dst, funct;
                        float ref;

                        fscanf(input, "%d/%d/%d/%f\n", &src, &dst, &funct, &ref);
                        P->setBlending(src, dst, funct, ref);
                    }
                    else if (strcmp(partline, "#ENDPART")== 0)
                        break;
                }

                if (P->HasTexture())
                    P->setTextureID(bindTexture(QPixmap(P->getTextureURL())));

                P->BuildSystem();
                ParticleSystems->append(P);

                emit ParticleSystemAdded(P->getName());
            }
            else
            {
                // Ignoremos comentarios y demas
                char ignore [1000];
                fgets(ignore, 1000, input);
            }

        }

        fclose(input);
    }
    else
        printf("ERROR: No se pudo leer la escena %s\n", url.toStdString().c_str());
}

/** Controles ------------------------------- **/
// Esta funcion permite saltar de 3D a 2D usando anaglifos
void GLWidget::SwitchAnaglyph()
{
    if (GOANAGLYPH)
    {
        GOANAGLYPH= false;
        Controller->SwitchDualRender(false);
        emit AnaglyphChanged(false); // Para actualizar los controles
    }
    else
    {
        GOANAGLYPH= true;
        Controller->SwitchDualRender(true);
        emit AnaglyphChanged(true); // Para actualizar los controles
    }
}

// Esta funcion permitira alterar el Zero Parallax a traves de la interfaz
void GLWidget::SetZeroParallax(double value)
{
    Controller->SetZeroParallax(float(value));
}

// Esta funcion permitira alterar la separacion focal a traves de la interfaz
void GLWidget::SetEyeDistance(double value)
{
    Controller->SetEyeDistance(float(value));
}

// Esta funcion permite saltar de 3D a 2D usando anaglifos
void GLWidget::SwitchAnaglyph(bool value)
{
    GOANAGLYPH= value;
    Controller->SwitchDualRender(value);
}

// Esta funcion permite activar el anaglifo Red-Cyan
void GLWidget::SwitchCyanAnaglyph(bool value)
{
    CYANANAGLYPH= value;
}

// // Esta funcion permite activar el anaglifo Red-Cyan
void GLWidget::SwitchCyanAnaglyph()
{
    if (CYANANAGLYPH)
    {
        CYANANAGLYPH= false;
        emit CyanAnaglyphChanged(false);
    }
    else
    {
        CYANANAGLYPH= true;
        emit CyanAnaglyphChanged(true);
    }
}

// Esta funcion enciende el rendering del mallado
void GLWidget::SwitchWireFrame(bool value)
{
    RENDERWIREFRAME= value;
}

// Esta funcion enciende el rendering del mallado en conjunto a sus poligonos
void GLWidget::SwitchOverlayWireFrame(bool value)
{
    OVERLAYWIREFRAME= value;
}

// Solo vamos a marcar las teclas presionadas
void GLWidget::keyPressEvent(QKeyEvent *e)
{
    if (e->key()==Qt::Key_Control)
    {
        TRACKMOUSE= !TRACKMOUSE;
        setMouseTracking(TRACKMOUSE);

        if (TRACKMOUSE)
        {
            Crosshair->setShape(Qt::BlankCursor);
            setCursor(*Crosshair);
        }
        else
        {
            Crosshair->setShape(Qt::CrossCursor);
            setCursor(*Crosshair);
        }
    }
    else if (e->key()==Qt::Key_F12)
    {
        SwitchAnaglyph();
    }
    else if (e->key()==Qt::Key_F11)
    {
        SwitchCyanAnaglyph();
    }
    else if (e->key()==Qt::Key_T)
    {
        emit LeftPanelSwitched();
    }
    else if (e->key()==Qt::Key_N)
    {
        emit RightPanelSwitched();
    }
    else
    {
        Keys[e->key()]= true;
    }
}

// Solo vamos a desmarcar las teclas presionadas
void GLWidget::keyReleaseEvent(QKeyEvent *e)
{
    Keys[e->key()]= false;
}

// Ahora simplemente vamos a revisar cuales han sido presionadas e informaremos al objeto de control
void GLWidget::timerEvent(QTimerEvent *e)
{
    // Movimientos de camara
    if (Keys[Qt::Key_W])// Adelante
        emit keypressed(Qt::Key_W, Crosshair->pos());
    if (Keys[Qt::Key_S])// Atras
        emit keypressed(Qt::Key_S, Crosshair->pos());
    if (Keys[Qt::Key_A])// Izquierda
        emit keypressed(Qt::Key_A, Crosshair->pos());
    if (Keys[Qt::Key_D])// Derecha
        emit keypressed(Qt::Key_D, Crosshair->pos());
    if (Keys[Qt::Key_Space])// Arriba
        emit keypressed(Qt::Key_Space, Crosshair->pos());
    if (Keys[Qt::Key_Shift])// Abajo
        emit keypressed(Qt::Key_Shift, Crosshair->pos());

    // Movimientos de la lampara
    if (Keys[Qt::Key_4]) // Luz a la Izquierda
        emit LightTweak(Qt::Key_4);
    if (Keys[Qt::Key_6]) // Luz a la Derecha
        emit LightTweak(Qt::Key_6);
    if (Keys[Qt::Key_8]) // Luz hacia adelante
        emit LightTweak(Qt::Key_8);
    if (Keys[Qt::Key_2]) // Luz hacia atras
        emit LightTweak(Qt::Key_2);
    if (Keys[Qt::Key_1]) // Luz hacia arriba
        emit LightTweak(Qt::Key_1);
    if (Keys[Qt::Key_0]) // Luz hacia abajo
        emit LightTweak(Qt::Key_0);
    if (Keys[Qt::Key_Plus]) // Luz + intensa
        emit LightTweak(Qt::Key_Plus);
    if (Keys[Qt::Key_Minus]) // Luz - intensa
        emit LightTweak(Qt::Key_Minus);
    if (Keys[Qt::Key_9]) // Acelerar movimiento de lampara
        emit LightTweak(Qt::Key_9);
    if (Keys[Qt::Key_3]) // Desacelerar movimiento de lampara
        emit LightTweak(Qt::Key_3);

    // Se movio el mouse?
    if (Keys[Qt::Key_Tab])
    {
        emit keypressed(Qt::Key_Tab, Crosshair->pos());
        Keys[Qt::Key_Tab]= false;
    }

    // Ahora devolvemos el mouse al centro de la pantalla
    if (TRACKMOUSE)
        Crosshair->setPos(mapToGlobal(QPoint(width()/2, height()/2)));

    updateGL();
}

void GLWidget::mouseMoveEvent(QMouseEvent *e)
{
    // Emitimos el movimiento con una tecla por default
    Keys[Qt::Key_Tab]= true;
    LastPoint.setX(e->pos().x());
    LastPoint.setY(e->pos().y());
}

// Esta funcion le indicara al widget que debe crear un objeto y cargar un archivo RAW con la informacion
// de un volumen, usando Marching Cubes para construirlo
void GLWidget::LoadRAW(QString URL,
                       VObject::NORMALMODE normcalc,
                       int xsize, int ysize, int zsize,
                       int bitdepth,
                       int isovalue,
                       int maxpoligon,
                       int xmax, int ymax, int zmax)
{   
    // Como nos han mandado a reconstruir un objeto, vamos a esconder toda la escena original

    // Eliminamos una posible reconstruccion anterior, guardando sus transformaciones
    QMatrix4x4 AuxMMatrix; bool REBUILDING;
    if (Objects->at(Objects->size()-1)->ObjType== VObject::MCRECONSTRUCTED)
    {
        AuxMMatrix= Objects->at(Objects->size()-1)->getModelMatrix();
        REBUILDING= true;
        Objects->remove(Objects->size()-1);
    }

    //Escondemos la escena original
    /*for (int i=0; i<Objects->size(); i++)
        Objects->at(i)->HideThis();*/

    Objects->at(0)->HideThis();
    Objects->at(3)->HideThis();

    // Y ahora vamos a crear el nuevo objeto
    VObject* Reconstruction= new VObject();

    // Esto nos dira el tiempo tomado por la reconstrucción
    QElapsedTimer ElapsedTime;
    ElapsedTime.start();

    int Result= Reconstruction->LoadRAWData(URL,
                                    normcalc,
                                    xsize, ysize, zsize,
                                    bitdepth,
                                    isovalue,
                                    maxpoligon,
                                    xmax, ymax, zmax);
    qint64 Elapsed= ElapsedTime.elapsed();

    if (Result== VObject::ALLOK || Result== VObject::WRONGTHRESHOLD)
    {
        if (Result== VObject::WRONGTHRESHOLD)
            emit WrongThreshold();

        if (Reconstruction->LoadShaders("../shaders/LameVShader.glsl", "../shaders/LameFShader.glsl"))
        {
            if (Reconstruction->PrepareShaderProgram())
            {
                Reconstruction->SetObjType(VObject::MCRECONSTRUCTED);
                Reconstruction->SetDiffuseColor(0.4, 0.405, 0.41);
                //Reconstruction->ScaleObj(3.5);
                //Reconstruction->SHOWTIMEROTATION= true;
                if (REBUILDING)
                    Reconstruction->modelMatrix*= AuxMMatrix;
                else
                    Reconstruction->TranslateObj(-xsize*Reconstruction->MCUnit, -ysize*Reconstruction->MCUnit/2.0, -zsize*Reconstruction->MCUnit/2.0);

                Objects->append(Reconstruction);

                emit ReconstructionInfo (int(xsize*ysize*zsize*bitdepth/8),
                                         QString("%1x%2x%3").arg(xsize).arg(ysize).arg(zsize),
                                         bitdepth,
                                         Objects->at(Objects->size()-1)->getPolygonCount(),
                                         Objects->at(Objects->size()-1)->getVertexCount(),
                                         isovalue,
                                         int (Elapsed));
            }
        }
    }
}

// Esta funcion simplemente voltea el volumen reconstruido
void GLWidget::UpsideDownMC()
{
    if (Objects->at(Objects->size()-1)->ObjType==VObject::MCRECONSTRUCTED)
    {
        Objects->at(Objects->size()-1)->rotMatrix.rotate(180.0, 1.0, 0.0, 0.0);
    }
}

// Esta funcion movera el volumen reconstruido en la direccion indicada por 0.2 unidades
void GLWidget::MoveMC(int dir)
{
    float unit= 0.2;

    // 1: arriba, -1: abajo, 2: izquierda, -2: derecha
    if (Objects->at(Objects->size()-1)->ObjType==VObject::MCRECONSTRUCTED)
    {
        VObject* auxObject=  Objects->at(Objects->size()-1);

        switch(dir)
        {
            case 1: auxObject->TranslateObj(0.0, unit, 0.0);
                    break;
            case -1: auxObject->TranslateObj(0.0, -unit, 0.0);
                    break;
            case 2: auxObject->TranslateObj(-unit, 0.0, 0.0);
                    break;
            case -2: auxObject->TranslateObj(unit, 0.0, 0.0);
                    break;
        };
    }
}


/** Getters **/
// Devuelve el objeto por el nombre solicitado
VObject*GLWidget::getObjectByName(QString input)
{
    for (int i=0; i<Objects->size(); i++)
        if (Objects->at(i)->objectName()== input) return Objects->at(i);

    for (int i=0; i<Lights->size(); i++)
        if (Lights->at(i)->objectName()== input) return Lights->at(i);

    // Si llegamos aca, es porque no hemos encontrado nada
    return NULL;
}

// Devuelve el sistema de particulas por el nombre solicitado
ParticleSystem* GLWidget::getPartSystemByName(QString input)
{
    for (int i=0; i<ParticleSystems->size(); i++)
        if (ParticleSystems->at(i)->getName()== input) return ParticleSystems->at(i);

    // Si llegamos aca, es porque no hemos encontrado nada
    return NULL;
}

// Esto altera la textura del sistema de particulas actual
bool GLWidget::ChangeParticleTexture(QString pname, QString url)
{
    for (int i=0; i<ParticleSystems->size(); i++)
        if (ParticleSystems->at(i)->getName()== pname)
        {
            ParticleSystem* P= ParticleSystems->at(i);

            P->setTextureURL(url);
            P->setTextureID(bindTexture(QPixmap(P->getTextureURL())));

            // Y guardamos el nombre limpio de la textura
            QStringList t= url.split("/");
            P->setTextureURL(t.at(t.size()-1));

            return true;
        }

    // Si llegamos aca, es porque no hemos encontrado nada
    return false;
}
